﻿using Microsoft.AspNet.Hosting;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.WindowsAzure.Storage.Blob;
using Sino.BankInfo;
using Sino.CapacityCloud.Repositories.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Sino.CapacityCloud.Services
{

    public class BankCardService
        : ConfigurableServiceBase, IBankCardService
    {

        public const string AlipayValidateAndCacheCardInfoUrl = "https://ccdcapi.alipay.com/validateAndCacheCardInfo.json?_input_charset=utf-8&cardNo={0}&cardBinCheck=true";

        private readonly IAzureStorageService _azureStorage;
        private readonly ILogger _logger;
        private readonly IBankCardInfoService _bankCardInfoService;
        private readonly IDistributedCache _cache;
        private readonly IServiceProvider _serviceProvider;

        private string _sas;

        public string BankLogosContainerName { get; }

        public string ApixKey { get; }

        public BankCardService(
            IAzureStorageService azureStorage,
            IHostingEnvironment env,
            ILoggerFactory loggerFactory,
            IServiceProvider serviceProvider,
            IDistributedCache cache,
            IBankCardInfoService bankCardInfoService)
            : base(env, section: "Data:DefaultConnection:BankCardService")
        {
            this._logger = loggerFactory.CreateLogger<BankCardService>();
            this._azureStorage = azureStorage;
            this._cache = cache;
            this._serviceProvider = serviceProvider;
            this._bankCardInfoService = bankCardInfoService;

            this.BankLogosContainerName = this.Configuration["BankLogosContainerName"];
            this.ApixKey = this.Configuration["ApixKey"];

            this._sas = this._azureStorage.GenerateShareAccessSignitureAsync("banklogos",
                SharedAccessBlobPermissions.Read |
                SharedAccessBlobPermissions.Write |
                SharedAccessBlobPermissions.List |
                SharedAccessBlobPermissions.Add |
                SharedAccessBlobPermissions.Create |
                SharedAccessBlobPermissions.Delete).GetAwaiter().GetResult();
        }

        public async Task<IDictionary<string, string>> GetBankInfosAsync()
        {
            return await Task.FromResult(this._bankCardInfoService.Banks.ToDictionary(x => x.Bin, x => x.BankName));
        }

        public Task<string> FetchBankImageKeyAsync(string bankCode, bool checkUpdate = false)
        {
            //return await Task.FromResult(string.Format(AlipayBankLogoUrl, bankCode));
            throw new NotImplementedException();
        }

        public async Task<CardInfo> VerifyBankCardAsync(string accountName, string cardNumber)
        {
            return await this._bankCardInfoService.QueryCardInfoAsync(this.ApixKey, cardNumber);
        }

        public async Task<BankCard> AddBankCardAsync(ApplicationUser user, string accountName, string bankCardNumber)
        {
            try
            {
                if (string.IsNullOrEmpty(bankCardNumber))
                {
                    return null;
                }
                var verify = await this.VerifyBankCardAsync(accountName, bankCardNumber);
                if (verify.Code == 0)
                {
                    using (var serviceScope = this._serviceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope())
                    {
                        using (var dbContext = serviceScope.ServiceProvider.GetService<ApplicationDbContext>())
                        {
                            var bankCard = dbContext.BankCards.FirstOrDefault(x => x.UserId == user.Id && x.Number == bankCardNumber);
                            if (bankCard != null)
                            {
                                throw new ArgumentException($"There is already a card with the save card number given under the user {user.Id}.");
                            }
                            var bank = dbContext.Banks.FirstOrDefault(x => x.Name == verify.Data.BankName);
                            if (bank == null)
                            {
                                bank = new Bank()
                                {
                                    Name = verify.Data.BankName,
                                    LogoKey = verify.Data.LogoUrl,
                                    Code = verify.Data.BankNum
                                };
                                dbContext.Banks.Add(bank);
                                dbContext.SaveChanges();
                            }
                            // TODO: Add CardType.
                            bankCard = new BankCard()
                            {
                                UserId = user.Id,
                                Holder = accountName,
                                Number = bankCardNumber,
                                Type = verify.Data.CardType,
                                BankId = bank.BankId
                            };
                            dbContext.BankCards.Add(bankCard);
                            dbContext.SaveChanges();
                            return bankCard;
                        }
                    }
                }
                throw new SystemException($"The given combination of account name and card number is not valid.");
            }
            catch (Exception ex)
            {
                this._logger.LogError($"Failed to add bank card because: {ex.Message + Environment.NewLine + ex.StackTrace}");
                return null;
            }
        }

    }

}